Repository navigation
Conversation
Document how to make a Workflows email template transactional and send it with POST /api/projects/:project_id/messaging_templates/:template_id/send/. The page covers the API key, the request and response, idempotency keys, error codes, rate limits, and how transactional emails behave. The page is marked beta. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Generated-By: PostHog Desktop Task-Id: a0274fa0-d9be-48bf-b601-f153decf241d
…e SDKs - Send through `POST /messaging/send/email/` with `template.id`. The template route still works. - Read an email's status with `GET /messaging/emails/:message_id/`: statuses, reasons, 404 cases, scopes and read limits. - Send and read with the Python and Node.js SDKs. - Add `recipient_unreachable` to the errors table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Generated-By: PostHog Desktop Task-Id: e72a2c10-f145-4f7c-8754-a4fcc06a18be
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Generated-By: PostHog Desktop Task-Id: e72a2c10-f145-4f7c-8754-a4fcc06a18be
- The Node.js example turns off local evaluation, and both examples keep the SDK's error. - The status examples handle the error first and explain retrying a not_found. - The SDK section adds install, client reuse, timeouts and retries, uncertain outcomes, and a no-network testing example. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Generated-By: PostHog Desktop Task-Id: e72a2c10-f145-4f7c-8754-a4fcc06a18be
…m pages The overview keeps the template, key, idempotency, errors, statuses and behavior. Node.js, Python and API pages hold the setup, send, status, retries and testing for each platform, with a sidebar group like push notifications. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Generated-By: PostHog Desktop Task-Id: e72a2c10-f145-4f7c-8754-a4fcc06a18be
…he overview - A quick start links to the Node.js, Python and API pages. - Creating an email follows the Transactional section's wizard, and the hidden workflow explanation is gone. - Agents can set it up with the PostHog MCP server. - Protect sensitive data turns off click tracking and keeps deliveries, opens and statuses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Generated-By: PostHog Desktop Task-Id: e72a2c10-f145-4f7c-8754-a4fcc06a18be
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Generated-By: PostHog Desktop Task-Id: e72a2c10-f145-4f7c-8754-a4fcc06a18be
The send endpoint now takes a flat body (PostHog/posthog#126287), so the field table, the curl example and the engagement events note use template_id and distinct_id. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Generated-By: PostHog Desktop Task-Id: e72a2c10-f145-4f7c-8754-a4fcc06a18be
…il API page Document the code form of the send route (template_key with the subject and body), capture_engagement, the checks that only apply to it, and a delivery_unavailable anchor for the 503's docs link. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mits Add DKIM-Signature and Received to the reserved headers, the header name and value limits, the tag length, and that reply_to takes bare addresses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…only Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t template_key and ph_ tags do - A retry with the same Idempotency-Key is the same request when every field of the code-form table matches. - template_key groups the email's metrics and sends. - A tag name that starts with ph_, in any case, is refused. - capture_engagement on a template send defaults to the project's setting. - Drop a double blank line after the checks table (fix-mdx). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ell out the ph_ case rule - The template-send table gets a capture_engagement row with the project default, and says a retry sends the field the same way. - The code-form row keeps only its own default. - A ph_ tag name is refused in uppercase or lowercase. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> # Conflicts: # contents/docs/workflows/transactional-emails/api.mdx # contents/docs/workflows/transactional-emails/node.mdx # contents/docs/workflows/transactional-emails/python.mdx
…code send The first release sends content from code with template_key, subject, html and text only. React Email output is rendered by the app and sent as html. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Drop capture_engagement from the template send: the send route refuses it there, with the other fields of content from your code. - Fold sender_not_allowed into sender_not_verified, the code the first release returns for a from address no verified sender can use. - Add "Send content from your code" to the Node.js and Python pages, with a React Email example rendered by the app. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… in a replay - A field error is a 400 with type validation_error, a field code such as required or invalid, and attr naming the field. - A replay also compares the sender PostHog chose, so a retry without from after the default sender changed returns idempotency_key_reused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ntify distinct ID Add "Link emails to your users" to the API, Node.js and Python pages: pass the distinct ID you pass to identify(), what happens without one today (a separate person keyed by the email address), and that invites and signup checks can leave it out. The overview and the API tables point there instead of "always send it". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Node.js and Python client examples used <ph_project_api_key> and a hard-coded ingestion host, which the site doesn't replace. They now use <ph_project_token> and <ph_client_api_host>, as the SDK pages do, so a logged-in reader sees their own project token and host. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…atus reads A send needs transactional_email:write and a status read needs transactional_email:read or :write. Keys with a hog_flow scope still work. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The key steps still picked Workflow, which creates a hog_flow key. The scopes list names the new scope Transactional email. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… email scope Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A status read right after a send can answer 404 for a few seconds, until the send's first status arrives. Say so where the API page lists the 404, and that a retry after a short wait is the fix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
posthog-python#1171 ships as a minor bump, and PyPI is already at 7.83.2, so the code send arrives in 7.84.0, not 7.83.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add a Nodemailer page under Send transactional emails: install, the PostHog transport, key and host, the posthog options, retries from a queue with a stable idempotency_key, and the messages PostHog refuses. Link it from the Node.js page and add a sidebar entry after Node.js. Ticket: PostHog/michaelr-context#41 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Deploy preview
Changed pages
|
|
Vale prose linter → found 0 errors, 5 warnings, 0 suggestions in your markdown Full report → Copy the linter results into an LLM to batch-fix issues. Linter being weird? Update the rules!
|
| Line | Severity | Message | Rule |
|---|---|---|---|
| 102:160 | warning | 'Nodemailer' is a possible misspelling. | PostHogBase.Spelling |
contents/docs/workflows/transactional-emails/nodemailer.mdx — 0 errors, 4 warnings, 0 suggestions
| Line | Severity | Message | Rule |
|---|---|---|---|
| 2:39 | warning | 'Nodemailer' is a possible misspelling. | PostHogBase.Spelling |
| 7:31 | warning | 'Nodemailer' is a possible misspelling. | PostHogBase.Spelling |
| 19:96 | warning | 'Nodemailer' is a possible misspelling. | PostHogBase.Spelling |
| 82:70 | warning | 'Nodemailer's' is a possible misspelling. | PostHogBase.Spelling |
There was a problem hiding this comment.
PostHog Review · Standard · reviewed 7c0f31f: 1 Should fix.
See this review and its findings in PostHog Review. Comment @posthog review --deep for a Deep review. A Deep review takes longer and looks deeper than a Standard review.
|
|
||
| - **`template_key`** names the email, such as `order-shipped`. PostHog groups the email's metrics and sends under it. Without it, the transport uses `defaultTemplateKey` from `createPostHogTransport`, or `nodemailer`. Use letters, digits, hyphens, and underscores. | ||
| - **`distinct_id`** records the email against the person with this distinct ID. Without it, PostHog uses a person whose distinct ID is the `to` address. See [link emails to your users](/docs/workflows/transactional-emails/node#link-emails-to-your-users). | ||
| - **`idempotency_key`** identifies this one email. PostHog sends an email only once for each key. |
There was a problem hiding this comment.
P2 · Limit the idempotency guarantee to its 24-hour window
When a queue retries a send more than 24 hours after PostHog queued it, the same idempotency_key no longer prevents another email. The transactional-email overview documents this expiration, but this unconditional guarantee tells readers that a stable key always prevents duplicates. State the 24-hour window and link the queue guidance to that limit.
Bundle reportTotal JS (gzip)10.27 MiB (-1480.1 KiB / -12.3%) Largest changed named chunks
Eager graph (modules shipped in each entrypoint's initial chunks)
Largest modules in the
|
| Module | Size |
|---|---|
./src/data/mcp-tools.json |
1491.9 KiB |
css ./node_modules/.pnpm/css-loader@5.2.7_webpack@5.101.3/node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[8].oneOf[1].use[1]!./node_modules/.pnpm/postcss-loader@4.3.0_postcss@8.5.6_webpack@5.101.3/node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[8].oneOf[1].use[2]!./src/styles/global.css |
838.0 KiB |
./src/components/Stickers/Stickers.tsx |
696.4 KiB |
./node_modules/.pnpm/@posthog+brand@0.13.0_react@18.3.1/node_modules/@posthog/brand/dist/generated/hoggies/svg/x-ray.mjs |
481.2 KiB |
./node_modules/.pnpm/@posthog+brand@0.13.0_react@18.3.1/node_modules/@posthog/brand/dist/generated/hoggies/svg/trenchcoat.mjs |
450.8 KiB |
./node_modules/.pnpm/rehype-raw@7.0.0/node_modules/rehype-raw/lib/index.js + 29 modules |
395.1 KiB |
./node_modules/.pnpm/@posthog+brand@0.13.0_react@18.3.1/node_modules/@posthog/brand/dist/generated/hoggies/svg/im-the-driver.mjs |
385.8 KiB |
./src/hooks/useCustomers.tsx + 61 modules |
381.8 KiB |
./node_modules/.pnpm/@posthog+brand@0.13.0_react@18.3.1/node_modules/@posthog/brand/dist/generated/hoggies/svg/tourist.mjs |
364.6 KiB |
./node_modules/.pnpm/@posthog+icons@0.36.6_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
354.8 KiB |
./node_modules/.pnpm/react-markdown@8.0.7_@types+react@16.14.66_react@18.3.1/node_modules/react-markdown/lib/react-markdown.js + 88 modules |
351.4 KiB |
./src/components/ProductComparisonTable/index.tsx + 130 modules |
314.3 KiB |
./node_modules/.pnpm/framer-motion@14.0.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/framer-motion/dist/es/render/components/motion/proxy.mjs + 126 modules |
287.2 KiB |
./node_modules/.pnpm/@posthog+brand@0.13.0_react@18.3.1/node_modules/@posthog/brand/dist/generated/hoggies/svg/doll-house.mjs |
284.1 KiB |
./node_modules/.pnpm/cloudinary-core@2.14.0_lodash@4.17.21/node_modules/cloudinary-core/cloudinary-core.js |
281.9 KiB |
Eager-graph budgets are report-only until a baseline is established. Sizes are gzip of public/**/*.js; eager size is webpack module source bytes for the modules actually shipped in the entrypoint's initial chunks (post-tree-shake).
Name the 24-hour key window for queued mail, and list the attachment refusals. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Changes
A developer whose app sends email with Nodemailer finds no page that tells them how to send that email through PostHog.
This PR adds Send transactional emails with Nodemailer under Send transactional emails. The page shows how to install
posthog-nodeandnodemailer, create the transport withcreatePostHogTransportfromposthog-node/nodemailer, and keep eachsendMailcall. It also covers the key and host, theposthogoptions for one message, a stableidempotency_keyfor mail from a job queue, and the messages that PostHog refuses (EPOSTHOGUNSUPPORTED). The Node.js page's code-send section links to the page, and the sidebar gets a Nodemailer entry after Node.js.Ticket: https://github.com/PostHog/michaelr-context/issues/41 (private).
Stacked on #21819. Merge it first. The transport ships in posthog-node with the PostHog/posthog-js draft PRs of the same ticket. This page stays a draft until that release is out.
Seams for review. The sidebar in
src/navs/index.jsis a hot file. #21932 adds SDK entries between Python and API, and #21984 adds React Email after API. This PR adds its entry after Node.js, so its hunk touches neither. Check for cross-stack breaks: the same concept documented twice (retries and idempotency keys on the Node.js page and on this page), and anything that conflicts with #21932 or #21984.Merge danger
Low, two-way door. Docs only: one new page, one link, and one sidebar entry. A revert removes them.
Evidence
prettier --check src/navs/index.jspasses.Decisions
refusalcode, with the fix where one existsChecklist
vercel.json(no page moved)🤖 Generated with Claude Code